package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.product.vo.SpuDetailStandardVO;
import cn.tedu.mall.pojo.product.vo.SpuStandardVO;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuDetailSimpleVO;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuVO;
import cn.tedu.mall.product.service.seckill.IForSeckillSpuService;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.service.ISeckillSpuService;
import cn.tedu.mall.seckill.utils.RedisBloomUtils;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SeckillSpuServiceImpl implements ISeckillSpuService {

    public static final String SECKILL_SPU_DETAIL_VO_PREFIX="seckill:spu:detail:vo:";

    @Autowired
    private SeckillSpuMapper seckillSpuMapper;

    //spu详细信息 是 pms_spu中，需要product远程调用
    @DubboReference
    private IForSeckillSpuService dubboSeckillSpuService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisBloomUtils redisBloomUtils;

    @Override
    public JsonPage<SeckillSpuVO> listSeckillSpus(Integer page, Integer pageSize) {
        //设置分页查询的条件
        PageHelper.startPage(page,pageSize);
        //执行查询方法
        List<SeckillSpu> seckillSpus = seckillSpuMapper.findSeckillSpus();
        //声明返回值类型
        ArrayList<SeckillSpuVO> seckillSpuVOS= new ArrayList<>();
        //seckillSpus集合转换成seckillSpuVOS集合
        for (SeckillSpu seckillSpu : seckillSpus) {
            //获取spu_id
            Long spuId = seckillSpu.getSpuId();
            //通过dubbo远程调用product模块
            SpuStandardVO standardVO = dubboSeckillSpuService.getSpuById(spuId);
            SeckillSpuVO seckillSpuVO = new SeckillSpuVO();
            BeanUtils.copyProperties(standardVO,seckillSpuVO);
            seckillSpuVO.setListPrice(seckillSpu.getListPrice());
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            seckillSpuVOS.add(seckillSpuVO);
        }
        return JsonPage.restPage(new PageInfo<>(seckillSpuVOS));
    }

    @Override
    public SeckillSpuVO getSeckillSpu(Long spuId) {
        //先判断spuId，是否在布隆过滤器中
        //先获取key
        String bloomTodayKey = SeckillCacheUtils.getBloomFilterKey(LocalDate.now());
        log.info("当前批次的布隆过滤器是：{}",bloomTodayKey);
        //如果不存在，则抛出异常
        if (!redisBloomUtils.bfexists(bloomTodayKey,spuId+"")){
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,"您访问的商品不存在（布隆过滤器中测试使用）");
        }
        //存在，继续往下执行
        String seckillSpuVOKey = SeckillCacheUtils.getSeckillSpuVOKey(spuId);
        SeckillSpuVO seckillSpuVO =null;
        if (redisTemplate.hasKey(seckillSpuVOKey)){
            seckillSpuVO = (SeckillSpuVO) redisTemplate.boundValueOps(seckillSpuVOKey).get();

        }else {
            //不在缓存中，查询数据库
            SeckillSpu seckillSpu = seckillSpuMapper.findSeckillSpuBySpuId(spuId);
            if (seckillSpu==null){
                throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,"您访问的商品不存在（布隆过滤器误判）");
            }
            //补全spu的信息
            SpuStandardVO spuStandardVO = dubboSeckillSpuService.getSpuById(spuId);
            seckillSpuVO=new SeckillSpuVO();
            BeanUtils.copyProperties(spuStandardVO,seckillSpuVO);
            seckillSpuVO.setListPrice(seckillSpu.getListPrice());//秒杀列表金额
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());//秒杀开启时间
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());//秒杀结束时间
            //seckillSpuVO缓存到redis中
            redisTemplate.boundValueOps(seckillSpuVOKey).set(seckillSpuVO,2*60*60+RandomUtils.nextInt(100),TimeUnit.SECONDS);
        }

        //秒杀商品不可以随时购买，需要在秒杀时间范围内，将随机码作为购买路径
        //需要判断当前时间是否在秒杀时间范围内

        LocalDateTime startTime = seckillSpuVO.getStartTime();
        LocalDateTime endTime = seckillSpuVO.getEndTime();
        LocalDateTime now = LocalDateTime.now();
        //now 是否大于startTime并且小于endTime
        if (now.isAfter(startTime) && now.isBefore(endTime)){
            //将随机码，赋值到url中
            //业务中设计的购买路径是/seckill/randCode
            //获取随机码
            String randCodeKey = SeckillCacheUtils.getRandCodeKey(spuId);
            int randCode = (int) redisTemplate.boundValueOps(randCodeKey).get();
            seckillSpuVO.setUrl("/seckill/"+randCode);
        }
        //将包含购买路径的seckillSpuVO返回
        return seckillSpuVO;
    }

    @Override
    public SeckillSpuDetailSimpleVO getSeckillSpuDetail(Long spuId) {
        String seckillSpuDetailKey = SECKILL_SPU_DETAIL_VO_PREFIX + spuId;
        SeckillSpuDetailSimpleVO seckillSpuDetailSimpleVO=null;
        if (redisTemplate.hasKey(seckillSpuDetailKey)){
            seckillSpuDetailSimpleVO=
                    (SeckillSpuDetailSimpleVO) redisTemplate.boundValueOps(seckillSpuDetailKey).get();
        }else {
            //查询数据库，存储到redis中
            SpuDetailStandardVO spuDetailStandardVO = dubboSeckillSpuService.getSpuDetailById(spuId);
            seckillSpuDetailSimpleVO=new SeckillSpuDetailSimpleVO();
            BeanUtils.copyProperties(spuDetailStandardVO,seckillSpuDetailSimpleVO);
            redisTemplate.boundValueOps(seckillSpuDetailKey)
                    .set(seckillSpuDetailSimpleVO,2*60*60+ RandomUtils.nextInt(100), TimeUnit.SECONDS);
        }
        return seckillSpuDetailSimpleVO;
    }
}
